package com.xuelang.pipeline.app.pipeline.impl;

import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.xuelang.hiforce.base.cache.client.service.DistributeCacheManager;
import com.xuelang.pipeline.app.pipeline.DeploymentUnitService;
import com.xuelang.pipeline.app.pipeline.converter.DeploymentUnitToDTOConverter;
import com.xuelang.pipeline.client.common.constant.Constant;
import com.xuelang.pipeline.client.common.constant.DictDTO;
import com.xuelang.pipeline.client.common.exception.BizException;
import com.xuelang.pipeline.client.common.request.PageParam;
import com.xuelang.pipeline.client.common.result.PageRecord;
import com.xuelang.pipeline.client.pipeline.dto.DeploymentUnitDTO;
import com.xuelang.pipeline.client.pipeline.dto.DeploymentUnitQuery;
import com.xuelang.pipeline.client.pipeline.enums.DeployStrategyEnum;
import com.xuelang.pipeline.client.pipeline.enums.DeployTaskFieldEnum;
import com.xuelang.pipeline.client.pipeline.enums.DeployUnitEnum;
import com.xuelang.pipeline.client.pipeline.enums.TaskInstanceEnum;
import com.xuelang.pipeline.domain.pipeline.DeploymentUnit;
import com.xuelang.pipeline.domain.pipeline.Pipeline;
import com.xuelang.pipeline.domain.pipeline.service.DeploymentUnitDomainService;
import com.xuelang.pipeline.domain.pipeline.service.PipelineDomainService;
import com.xuelang.pipeline.domain.resource.Cluster;
import com.xuelang.pipeline.domain.resource.Environment;
import com.xuelang.pipeline.domain.resource.Space;
import com.xuelang.pipeline.domain.resource.service.ClusterDomainService;
import com.xuelang.pipeline.domain.resource.service.EnvironmentDomainService;
import com.xuelang.pipeline.domain.resource.service.SpaceDomainService;
import com.xuelang.pipeline.domain.task.TaskInstance;
import com.xuelang.pipeline.domain.task.service.TaskInstanceDomainService;
import com.xuelang.pipeline.worker.client.common.result.ApiResult;
import com.xuelang.pipeline.worker.client.deployment.IDeploymentService;
import com.xuelang.pipeline.worker.client.deployment.dto.DeployQueryDTO;
import io.kubernetes.client.openapi.models.V1Scale;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * <p>部署单服务实现类</p>
 *
 * @author : yixin.lys
 * @date : 2024-06-25 18:13
 **/
@Service
public class DeploymentUnitServiceImpl implements DeploymentUnitService {

    @Autowired
    private DeploymentUnitDomainService deploymentUnitDomainService;
    @Autowired
    private IDeploymentService deploymentService;
    @Autowired
    private TaskInstanceDomainService taskInstanceDomainService;
    @Autowired
    private DistributeCacheManager redisHelper;
    @Autowired
    private PipelineDomainService pipelineDomainService;
    @Autowired
    private EnvironmentDomainService environmentDomainService;
    @Autowired
    private SpaceDomainService spaceDomainService;
    @Autowired
    private ClusterDomainService clusterDomainService;

    @Override
    public PageRecord<DeploymentUnitDTO> page(PageParam<DeploymentUnitQuery> query) {
        Long currentPage = query.getCurrentPage();
        Long pageSize = query.getPageSize();
        DeploymentUnitQuery condition = query.getCondition();
        PageRecord<DeploymentUnit> pageResult = deploymentUnitDomainService.pageByCondition(currentPage, pageSize, condition);
        List<DeploymentUnitDTO> dtoPageResult = pageResult.getRecords().stream().map(DeploymentUnitToDTOConverter.INSTANCE::toDTO).collect(Collectors.toList());
        List<DeploymentUnitDTO> finalPageResult = dtoPageResult.stream().map(d -> {
            TaskInstance taskInstance = taskInstanceDomainService.queryByTaskCode(d.getTaskInstanceCode());
            if(Objects.nonNull(taskInstance)) {
                d.setTaskInstanceStatus(TaskInstanceEnum.fromKey(taskInstance.getTaskStatus()).getValue());
            }
            return d;
        }).collect(Collectors.toList());
        return new PageRecord<>(
                pageResult.getCurrentPage(),
                pageResult.getPageSize(),
                pageResult.getTotalCount(),
                pageResult.getTotalPages(),
                finalPageResult
        );
    }

    @Override
    public void add(DeploymentUnitDTO deploymentUnit) {
        deploymentUnitDomainService.add(DeploymentUnitToDTOConverter.INSTANCE.fromDTO(deploymentUnit));
    }

    @Override
    public void update(DeploymentUnitDTO deploymentUnit) {
        deploymentUnitDomainService.update(DeploymentUnitToDTOConverter.INSTANCE.fromDTO(deploymentUnit));
    }

    @Override
    public DeploymentUnitDTO queryByPipelineInstanceCode(String pipelineInstanceCode) {
        return DeploymentUnitToDTOConverter.INSTANCE
                .toDTO(deploymentUnitDomainService.queryByPipelineInstanceCode(pipelineInstanceCode));
    }

    @Override
    public void removeByIds(List<String> idList) {
        deploymentUnitDomainService.removeByIds(idList);
    }

    @Override
    public void removeByCodes(List<String> codeList) {
        deploymentUnitDomainService.removeByCodes(codeList);
    }

    @Override
    public Boolean continueDeployment(String id) {
        Assert.notEmpty(id, "id不能为空");
        DeploymentUnit deploymentUnit = deploymentUnitDomainService.queryById(id);
        //继续灰度要做的操作，1.校验是否能点击继续  2. 根据任务实例和部署单 得出这个几个信息， 第几批，更新的副本数和需要跟新的deployment旧版本和新版本的名字
        String instanceTaskCode = deploymentUnit.getTaskInstanceCode();
        TaskInstance taskInstance = taskInstanceDomainService.queryByTaskCode(instanceTaskCode);
        HashMap<String, String> hashMap = JSONObject.parseObject(taskInstance.getParams(), HashMap.class);
        String deployStrategy = hashMap.get(DeployTaskFieldEnum.deployStrategy.getKey());
        if (deployStrategy.equals(DeployStrategyEnum.Rolling_publish.getKey())) {
            throw new BizException("滚动发布无需触发");
        }
        if (!TaskInstanceEnum.success.getKey().equals(taskInstance.getTaskStatus())) {
            throw new BizException("部署失败无需触发");
        }
        Integer bathNum = new BigDecimal(String.valueOf(hashMap.get(DeployTaskFieldEnum.batchesNum.getKey()))).intValueExact();
        Integer nowBath = Integer.valueOf(deploymentUnit.getDeploymentProgress());
        if (bathNum.equals(nowBath)) {
            throw new BizException("当前已是最后一批");
        }
        Integer oldReplicas = 0;
        Integer newReplicas = deploymentUnit.getTargetReplicas();
        if (!bathNum.equals(nowBath + 1)) {
            //非最后一次
            newReplicas = deploymentUnit.getTargetReplicas() / bathNum * (nowBath + 1);
            oldReplicas = deploymentUnit.getTargetReplicas() - newReplicas;
        }

        Pipeline pipeline = pipelineDomainService.query(deploymentUnit.getProgramVersion());
        String componentLabel = pipeline.getComponentLabel();
        Integer version = Math.toIntExact(redisHelper.incrby(Constant.GRAY_DEPLOYMENT_VERSION_KEY + componentLabel, 0));
        String newDeployName = componentLabel + "-" + version;
        String oldDeployName;
        if (version.equals(1)) {
            oldDeployName = componentLabel;
        }
        oldDeployName = componentLabel + "-" + (version - 1);
        Environment environment = environmentDomainService.query(deploymentUnit.getEnvironmentUuid());
        Space space = spaceDomainService.query(environment.getSpaceUuid());
        Cluster cluster = clusterDomainService.query(space.getClusterUuid());

        String newbody = "[{\"op\":\"replace\",\"path\":\"/spec/replicas\",\"value\": " + newReplicas + "}]";
        String oldBody = "[{\"op\":\"replace\",\"path\":\"/spec/replicas\",\"value\": " + oldReplicas + "}]";
        DeployQueryDTO deployQueryDTO = new DeployQueryDTO();
        deployQueryDTO.setKubeConfig(cluster.getKubeConfig());
        deployQueryDTO.setDeploymentName(newDeployName);
        deployQueryDTO.setNamespaceName(space.getNamespace());
        deployQueryDTO.setBody(newbody);
        ApiResult<V1Scale> newDeployment = deploymentService.patchDeployment(deployQueryDTO);
        deployQueryDTO.setDeploymentName(oldDeployName);
        deployQueryDTO.setBody(oldBody);
        ApiResult<V1Scale> oldDeployment = deploymentService.patchDeployment(deployQueryDTO);
        deploymentUnit.setDeploymentProgress(DeployUnitEnum.fromKey(String.valueOf(nowBath + 1)).getKey());
        deploymentUnitDomainService.update(deploymentUnit);
        return true;
    }

    @Override
    public Boolean rollback(String id) {
        Assert.notEmpty(id, "id不能为空");
        DeploymentUnit deploymentUnit = deploymentUnitDomainService.queryById(id);
        //继续灰度要做的操作，1.校验是否能点击继续  2. 根据任务实例和部署单 得出这个几个信息， 第几批，更新的副本数和需要跟新的deployment旧版本和新版本的名字
        String instanceTaskCode = deploymentUnit.getTaskInstanceCode();
        TaskInstance taskInstance = taskInstanceDomainService.queryByTaskCode(instanceTaskCode);
        HashMap<String, String> hashMap = JSONObject.parseObject(taskInstance.getParams(), HashMap.class);
        String deployStrategy = hashMap.get(DeployTaskFieldEnum.deployStrategy.getKey());
        if (deployStrategy.equals(DeployStrategyEnum.Rolling_publish.getKey())) {
            throw new BizException("滚动发布无需触发");
        }
        if (!TaskInstanceEnum.success.getKey().equals(taskInstance.getTaskStatus())) {
            throw new BizException("部署失败无需触发");
        }
        if (DeployUnitEnum.zero.getKey().equals(deploymentUnit.getDeploymentProgress())) {
            throw new BizException("已回滚，无需再次触发");
        }
        Integer oldReplicas = deploymentUnit.getTargetReplicas();
        Integer newReplicas = 0;

        Pipeline pipeline = pipelineDomainService.query(deploymentUnit.getProgramVersion());
        String componentLabel = pipeline.getComponentLabel();
        Integer version = Math.toIntExact(redisHelper.incrby(Constant.GRAY_DEPLOYMENT_VERSION_KEY + componentLabel, 0));
        String newDeployName = componentLabel + "-" + version;
        String oldDeployName;
        if (version.equals(1)) {
            oldDeployName = componentLabel;
        }
        oldDeployName = componentLabel + "-" + (version - 1);
        Environment environment = environmentDomainService.query(deploymentUnit.getEnvironmentUuid());
        Space space = spaceDomainService.query(environment.getSpaceUuid());
        Cluster cluster = clusterDomainService.query(space.getClusterUuid());

        String newbody = "[{\"op\":\"replace\",\"path\":\"/spec/replicas\",\"value\": " + newReplicas + "}]";
        String oldBody = "[{\"op\":\"replace\",\"path\":\"/spec/replicas\",\"value\": " + oldReplicas + "}]";
        DeployQueryDTO deployQueryDTO = new DeployQueryDTO();
        deployQueryDTO.setKubeConfig(cluster.getKubeConfig());
        deployQueryDTO.setDeploymentName(newDeployName);
        deployQueryDTO.setNamespaceName(space.getNamespace());
        deployQueryDTO.setBody(newbody);
        ApiResult<V1Scale> newDeployment = deploymentService.patchDeployment(deployQueryDTO);
        deployQueryDTO.setDeploymentName(oldDeployName);
        deployQueryDTO.setBody(oldBody);
        ApiResult<V1Scale> oldDeployment = deploymentService.patchDeployment(deployQueryDTO);
        deploymentUnit.setDeploymentProgress(DeployUnitEnum.zero.getKey());
        deploymentUnitDomainService.update(deploymentUnit);
        return true;
    }

    @Override
    public List<DictDTO> deploymentProcessStatus() {
        List<DictDTO> dtoList = new ArrayList<>();
        for (DeployUnitEnum deployUnitEnum : DeployUnitEnum.values()) {
            DictDTO dict = new DictDTO();
            dict.setId(deployUnitEnum.getKey());
            dict.setText(deployUnitEnum.getName());
            dtoList.add(dict);
        }
        return dtoList;
    }
}
